Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

URnetwork Interface Localizations

The single source of truth for every string in every URnetwork interface: android, apple, windows, linux and the web extension.

keys/*.yaml is the store. Everything else is generated from it — the app resource files are build artifacts and must never be hand-edited:

platformgenerated file(s)
androidandroid/app/app/src/main/res/values*/strings.xml
appleapple/app/network/Shared/Resources/Localizable.xcstrings
windowswindows/app/src/App/Strings/<locale>/Resources.resw
linuxlinux/app/po/*.po, linux/app/po/urnetwork.pot
webconsumed directly from keys/ by index.js (no codegen)
npm ci
npm run gen # regenerate every platform
npm run check # CI drift gate: fail if the committed output != the store

npm run gen writes into the sibling repo checkouts (../android, ../apple, …). Set URNETWORK_ROOT if they live somewhere else. The release build runs it on every build (see build/all/run.sh).

Schema

One file per key. The file name is the key id (snake_case, and also the android resource name, the .resw name and the gettext msgctxt).

description: "A label that shows the number of hosts for a split rule."description_auto: true # optional: xcode generated this comment, it may replace itsource: "%lld hosts"# REQUIREDtranslatable: false # optional, default truedeprecated: # optional: platforms that no longer reference the key
- appleplatforms: # which platforms reference the key today
- android
- applealiases: # optional: legacy ids that must keep resolving
- host_count_oldplaceholders: # optional, ordered
- name: counttype: int # string | int | floatprecision: 3# optional, float onlyplurals: # optionalselect: count # the int placeholder that selects the CLDR categorylocalizations:
en:
one: "{count} host"other: "{count} hosts"ar:
zero: "{count} مضيف"one: "مضيف واحد"# ...

source

The English source text exactly as it appears in the apple string catalog, i.e. the literal a SwiftUI Text("…") / String(localized: "…") produces. It is the xcstrings key, so changing it breaks the swift call sites: change the call site and source together.

It is normally identical to localizations.en, but not always — xcode stores the raw literal as the key and a normalised form as the value ("Don't see it? ""Don't see it?", "Claim multiplier""Claim Multiplier"). Both are kept.

For keys that are not on apple, source is simply the English text.

placeholders

Canonical, named, ICU-style placeholders ({count}, {code}). Ordered: the order here is the argument order. Codegen lowers them per platform:

typeandroidapplewindows / linux (C++)
string%1$s%@{}
int%1$d%lld or %d{}
float + precision: 3%1$.3f%.3lf{}

Apple uses whichever conversion source already contains (%d for a Swift Int32, %lld for an Int), so the key stays byte-exact. Values with two or more placeholders are emitted positionally (%1$@ … %2$@, {0} … {1}) so that a translation may reorder them.

Markers that are not placeholders ({link}, {terms_start}) pass through untouched — only names declared in placeholders: are substituted.

plurals

plurals.select names the int placeholder that selects the category, and each locale's value becomes a map of CLDR cardinal categories instead of a string. Only the categories a language actually has may be used — ar has zero/one/two/few/many/other, ja/ko/zh/th have only other. The generator emits android <plurals>, apple "variations": { "plural": … }, <id>.<category> entries in .resw, and gettext msgid_plural / msgstr[n].

A non-other category may omit the placeholder — several languages spell the count out ("مضيف واحد" = "one host") rather than printing the digit.

Never inflect a count in the interpolation ("\(n) host\(n == 1 ? "" : "s")"): it bakes English morphology into the string and is untranslatable in every other language. Use a plural key.

translatable: false

Product names and non-UI literals: URnetwork, UR, Bittensor, Solana, USDC, TAO, Polygon, Stripe and the like. They are emitted only into the base (English) file — android gets translatable="false", apple gets "shouldTranslate": false — and never reach a translator.

npm run check also enforces the weaker rule for prose: if the English contains a product name, every translation must contain it too, unchanged. (values-fr used to ship URréseau, values-deURnetzwerk, values-elURδίκτυο…)

aliases

Published web ids that lost their file name to an android id — android's id wins (continue is not even a legal android resource name, hence continue_txt). index.js resolves an alias to the same key object, so chrome.i18n.getMessage("continue") keeps working.

deprecated

Platforms that no longer reference the key. Apple's catalog keeps such entries and marks them extractionState: stale — xcode's own behaviour. The key is not deleted, so no translation is ever lost.

Locales

28: en ar cs de el es es-419 es-MX fr he hi id it ja ko nl pl pt pt-BR pt-PT ru sv sw th uk vi zh zh-HK

BCP-47, except that Simplified Chinese is zh (what android's values-zh and the published web package already use). zh-HK is Traditional.

Each platform emits the locales it actually supports; the maps live in gen/store.mjs:

  • android — the 19 res/values* dirs (es-MXvalues-es-rMX, pt-BRvalues-pt-rBR, zhvalues-zh, …).
  • apple — the 27 in the catalog (zhzh-Hans). sw is absent from the xcode project's knownRegions, so it is not emitted; adding it means adding it to APPLE_LOCALEandknownRegions.
  • windows / linux — all 28.

Adding a locale to a platform = adding it to that map. The translations are already in the store.

Web

index.js reads keys/ directly. loadAllKeys() returns the keys that opt into platforms: [web] (plus their aliases), which is exactly the set the extension had before the store absorbed the app corpora; loadAllKeys({ platform: null }) returns everything. Values are always strings — a plural key collapses to its other form, with the full category map still on key.plurals.forms.

Development

You can run npm pack to bundle it locally and then npm install ./path-to-localizations-archive.tgz to install it into your URnetwork interface project for testing.

Deployment

  • Make sure everything is committed and pushed to the main branch.
  • Make sure you're logged into npm with npm login.
  • Run npm pack --dry-run to see what will be included in the package.
  • If everything looks good, run npm run release:patch to publish the package. There are also release:minor and release:major scripts available for versioning.

About

Centralized localizations for all URnetwork interfaces

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages