chore: change how do we pass customized message

This commit is contained in:
anatawa12 2026-04-15 23:55:39 +09:00
commit 4e31d23b0c
No known key found for this signature in database
GPG key ID: 9CA909848B8E4EA6
7 changed files with 82 additions and 76 deletions

View file

@ -84,21 +84,6 @@ nix = { version = "0.31", features = ["fs"] }
# DO NOT REMOVE!!
custom-protocol = [ "tauri/custom-protocol" ]
# Feature flags for ALCOM.
# Some flags are negative flag that is not recommended by cargo,
# but our app is not library so OK for now.
# Disable updater
# This feature flag disables the built-in updater feature.
# Useful when ALCOM is managed by a package manager.
#
# It is recommended to set the following build-time environment variables:
# ALCOM_UPDATE_UPDATER_DISABLED_MESSAGE
# ALCOM_UPDATE_UPDATER_DISABLED_${LOCALE}_MESSAGE
# where ${LOCALE} is the language name in uppercase.
#
# These variables should instruct users how to update ALCOM
# using the package manager provided by the distribution.
no-self-updater = []
# Devtools

View file

@ -9,7 +9,7 @@ import { commands } from "@/lib/bindings";
import { callAsyncCommand } from "@/lib/call-async-command";
import type { DialogContext } from "@/lib/dialog";
import globalInfo from "@/lib/global-info";
import { tc } from "@/lib/i18n";
import { localizeExternalComponent, tc } from "@/lib/i18n";
type ConfirmStatus =
| {
@ -128,10 +128,10 @@ export function CheckForUpdateMessage({
"check update:dialog:new version updater disabled base description",
)}
<br />
{tc([
"from-env:updater disabled message",
"check update:dialog:new version updater how to upgrade fallback",
])}
{localizeExternalComponent(response.updater_disabled_messages, {
localized:
"check update:dialog:new version updater how to upgrade fallback",
})}
</p>
);
break;

View file

@ -313,7 +313,7 @@ async deepLinkReduceImportedClearNonToastedCount(reduce: number) : Promise<void>
export type AddRepositoryInfo = { url: string; headers: { [key in string]: string } }
export type AsyncCallResult<P, R> = { type: "Result"; value: R } | { type: "Started" } | { type: "UnusedProgress"; progress: P }
export type CheckForUpdateResponse = { version: number; current_version: string; latest_version: string; updater_status: UpdaterStatus; update_description: string | null }
export type CheckForUpdateResponse = { version: number; current_version: string; latest_version: string; updater_status: UpdaterStatus; update_description: string | null; updater_disabled_messages: { [key in string]: string } | null }
/**
* Errors that is expected to be handled on the GUI side
*/

View file

@ -22,26 +22,6 @@ const languageResources = {
zh_hant: zh_hantJson,
};
// merge messages from environment
function mergeMessage(
envMessages: Record<string, string> | null,
key: string,
languageResources: Record<string, Record<string, Record<string, unknown>>>,
) {
if (envMessages == null) return;
for (const [locale, message] of Object.entries(envMessages)) {
const localeMessages = languageResources[locale]?.translation;
if (localeMessages) {
localeMessages[key] = message;
}
}
}
mergeMessage(
ALCOM_UPDATE_UPDATER_DISABLED_MESSAGE,
"from-env:updater disabled message",
languageResources as never,
);
i18next.use(initReactI18next).init({
resources: languageResources as Resource,
lng: "en",
@ -88,3 +68,72 @@ export function tc(
}
export const tt = i18nextt;
// Helper component, type, and function for externally provided localization
// Key is name of locale, value is message in its locale.
type ExternalLocalization = Record<string, string> | null;
type Fallback = { plain: string } | { localized: string };
function localizeExternalImpl(
i18n: typeof i18next,
localization: ExternalLocalization,
): string | undefined {
if (localization == null) return undefined;
for (const language of i18n.languages) {
// biome-ignore lint/suspicious/noPrototypeBuiltins: we're targeting 2021
if (Object.prototype.hasOwnProperty.call(i18n, language)) {
const localized = localization[language];
if (localized) {
return localized;
}
}
}
return undefined;
}
export function localizeExternal(
localization: ExternalLocalization,
fallback: Fallback,
) {
const localized = localizeExternalImpl(i18next, localization);
if (localized) {
return localized;
}
if ("plain" in fallback) {
return fallback.plain;
}
return i18next.t(fallback.localized);
}
function LocalizeExternalComponentImpl({
localization,
fallback,
}: {
localization: ExternalLocalization;
fallback: Fallback;
}) {
const { i18n } = useTranslation();
const localized = localizeExternalImpl(i18n, localization);
if (localized) {
return React.createElement(Trans, {
defaults: localized,
components: { ExternalLink: React.createElement(ExternalLink) },
});
}
if ("plain" in fallback) {
return fallback.plain;
}
return i18n.t(fallback.localized);
}
export function localizeExternalComponent(
localization: ExternalLocalization,
fallback: Fallback,
) {
return React.createElement(LocalizeExternalComponentImpl, {
localization,
fallback,
});
}

View file

@ -80,6 +80,7 @@ pub struct CheckForUpdateResponse {
latest_version: String,
updater_status: updater::UpdaterStatus,
update_description: Option<String>,
updater_disabled_messages: Option<indexmap::IndexMap<String, String>>,
}
#[tauri::command]
@ -97,6 +98,11 @@ pub async fn util_check_for_update(
let latest_version = response.version.clone();
let updater_status = response.updater_status;
let update_description = response.body.clone();
let updater_disabled_messages = if cfg!(feature = "no-self-updater") {
option_env!("ALCOM_UPDATER_DISABLED_MESSAGE").and_then(|x| serde_json::from_str(x).ok())
} else {
None
};
let version = updater_state.set(response);
Ok(Some(CheckForUpdateResponse {
@ -105,6 +111,7 @@ pub async fn util_check_for_update(
latest_version,
updater_status,
update_description,
updater_disabled_messages,
}))
}

View file

@ -1,5 +1 @@
/// <reference types="vite/client" />
declare const ALCOM_UPDATE_UPDATER_DISABLED_MESSAGE: Record<
string,
string
> | null;

View file

@ -43,35 +43,4 @@ export default defineConfig({
},
},
clearScreen: false,
define: {
ALCOM_UPDATE_UPDATER_DISABLED_MESSAGE: JSON.stringify(
makeEnvMessageTable("ALCOM_UPDATE_UPDATER_DISABLED"),
),
},
});
function makeEnvMessageTable(envName: string): Record<string, string> | null {
const env = process.env;
const english = env[`${envName}_MESSAGE`];
// first check for en message
if (!english) return null;
// there is english message. We'll add other languages as well
const result: Record<string, string> = {};
const regex = new RegExp(`^${envName}_(?<locale>[A-Z_]+)_MESSAGE$`);
for (const [envName, envValue] of Object.entries(process.env)) {
if (!envValue) continue;
const matchResult = regex.exec(envValue);
if (matchResult == null) continue;
// biome-ignore lint/style/noNonNullAssertion: we have defined match group.
const localeName = matchResult.groups!.locale.toLowerCase();
if (localeName.length === 0) continue;
result[localeName] = envValue;
}
result.en = english;
return result;
}