From 4ffdf01f6af28020a00949c0509594d01df43181 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Mon, 21 Sep 2026 11:04:40 +0100 Subject: [PATCH 1/3] fix(lint): make the internal lint test suite runnable outside Windows --- internal/lint/build.gradle.kts | 1 + .../firebaseui/lint/internal/LintTestSdk.kt | 44 +++++++++++++++++++ .../lint/internal/NonGlobalIdDetectorTest.kt | 13 +----- 3 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 internal/lint/src/test/java/com/firebaseui/lint/internal/LintTestSdk.kt diff --git a/internal/lint/build.gradle.kts b/internal/lint/build.gradle.kts index 352a272573..f385f37dbf 100644 --- a/internal/lint/build.gradle.kts +++ b/internal/lint/build.gradle.kts @@ -6,6 +6,7 @@ dependencies { compileOnly(libs.lint.api) compileOnly(libs.kotlin.stdlib) + testImplementation(libs.junit) testImplementation(libs.lint.api) testImplementation(libs.lint.tests) } diff --git a/internal/lint/src/test/java/com/firebaseui/lint/internal/LintTestSdk.kt b/internal/lint/src/test/java/com/firebaseui/lint/internal/LintTestSdk.kt new file mode 100644 index 0000000000..88d4825b02 --- /dev/null +++ b/internal/lint/src/test/java/com/firebaseui/lint/internal/LintTestSdk.kt @@ -0,0 +1,44 @@ +package com.firebaseui.lint.internal + +import com.android.tools.lint.checks.infrastructure.TestLintTask +import java.io.File +import java.util.Properties + +/** + * Points [TestLintTask] at the local Android SDK. + * + * Lint's test harness refuses to run without one, and Gradle does not put `ANDROID_HOME` into + * the test JVM's environment. The previous approach scanned `java.library.path` for a segment + * containing "SDK", splitting on `;`, which only ever resolved on Windows: on macOS and Linux + * it silently found nothing, the SDK went unconfigured, and every test in this module failed + * with "This test requires an Android SDK". + */ +internal fun TestLintTask.withLocalSdk(): TestLintTask { + val sdk = androidSdkHome() ?: error( + "No Android SDK found. Set ANDROID_HOME, or add sdk.dir to local.properties at the " + + "repository root." + ) + return sdkHome(sdk) +} + +private fun androidSdkHome(): File? { + val fromEnv = sequenceOf("ANDROID_HOME", "ANDROID_SDK_ROOT") + .mapNotNull { System.getenv(it) } + .map(::File) + .firstOrNull(File::isDirectory) + if (fromEnv != null) return fromEnv + + // Tests run with the module directory as the working directory, so walk up to the root. + var dir: File? = File(".").absoluteFile + while (dir != null) { + val properties = File(dir, "local.properties") + if (properties.isFile) { + val sdkDir = properties.inputStream().use { Properties().apply { load(it) } } + .getProperty("sdk.dir") + ?.let(::File) + if (sdkDir?.isDirectory == true) return sdkDir + } + dir = dir.parentFile + } + return null +} diff --git a/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt b/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt index 5bdf360c3b..793efedf3f 100644 --- a/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt +++ b/internal/lint/src/test/java/com/firebaseui/lint/internal/NonGlobalIdDetectorTest.kt @@ -4,21 +4,10 @@ import com.android.tools.lint.checks.infrastructure.TestFiles.xml import com.android.tools.lint.checks.infrastructure.TestLintTask import com.firebaseui.lint.internal.NonGlobalIdDetector.Companion.NON_GLOBAL_ID import org.junit.Test -import java.io.File class NonGlobalIdDetectorTest { - // Nasty hack to make lint tests pass on Windows. For some reason, lint doesn't - // automatically find the Android SDK in its standard path on Windows. This hack looks - // through the system properties to find the path defined in `local.properties` and then - // sets lint's SDK home to that path if it's found. - private val sdkPath = System.getProperty("java.library.path").split(';').find { - it.contains("SDK", true) - } - - fun configuredLint(): TestLintTask = TestLintTask.lint().apply { - sdkHome(File(sdkPath ?: return@apply)) - } + fun configuredLint(): TestLintTask = TestLintTask.lint().withLocalSdk() @Test fun `Passes on valid view id`() { From 2447342122715a9e6dc751da748839ce210c1e12 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Mon, 21 Sep 2026 11:04:40 +0100 Subject: [PATCH 2/3] feat(lint): flag localized strings that still hold the base English text --- auth/build.gradle.kts | 6 + auth/src/main/res/values-da/strings.xml | 3 +- auth/src/main/res/values-fil/strings.xml | 4 +- auth/src/main/res/values-it/strings.xml | 4 +- auth/src/main/res/values-nb/strings.xml | 3 +- auth/src/main/res/values-no/strings.xml | 3 +- auth/src/main/res/values-pt-rPT/strings.xml | 4 +- auth/src/main/res/values-tl/strings.xml | 4 +- .../lint/internal/LintIssueRegistry.kt | 3 +- .../internal/UntranslatedResourceDetector.kt | 175 +++++++++++++++++ .../UntranslatedResourceDetectorTest.kt | 184 ++++++++++++++++++ 11 files changed, 381 insertions(+), 12 deletions(-) create mode 100644 internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt create mode 100644 internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt diff --git a/auth/build.gradle.kts b/auth/build.gradle.kts index 0bc26ceb51..60390d512f 100644 --- a/auth/build.gradle.kts +++ b/auth/build.gradle.kts @@ -152,6 +152,12 @@ dependencies { testImplementation(libs.kotlinx.serialization.json) debugImplementation(project(":internal:lintchecks")) + + // Directly, not via :internal:lintchecks. That module declares lintChecks too, but + // lintChecks only applies to the module declaring it, and a debugImplementation + // dependency does not carry lint checks to the consumer — so the custom rules were never + // running here. + lintChecks(project(":internal:lint")) } kotlin { diff --git a/auth/src/main/res/values-da/strings.xml b/auth/src/main/res/values-da/strings.xml index 7ae951c406..1940569d05 100755 --- a/auth/src/main/res/values-da/strings.xml +++ b/auth/src/main/res/values-da/strings.xml @@ -62,7 +62,8 @@ Gendan adgangskode Tjek din mail. Få en vejledning sendt til denne mail om, hvordan du nulstiller din adgangskode. - Send + + Send Følg vejledningen, der blev sendt til %1$s, for at gendanne din adgangskode. Sender… Mailadressen matcher ikke en eksisterende konto diff --git a/auth/src/main/res/values-fil/strings.xml b/auth/src/main/res/values-fil/strings.xml index 3dda0a5b3e..0c582986c8 100755 --- a/auth/src/main/res/values-fil/strings.xml +++ b/auth/src/main/res/values-fil/strings.xml @@ -10,7 +10,7 @@ Twitter GitHub Telepono - Email + Email Mag-sign in sa Google Mag-sign in sa Google Mag-sign in sa Facebook @@ -36,7 +36,7 @@ Bansa Pumili ng bansa Maghanap ng bansa hal. +1, "US" - Password + Password Bagong password Hindi mo ito maaaring iwanan na walang laman. Mali ang email address na iyon diff --git a/auth/src/main/res/values-it/strings.xml b/auth/src/main/res/values-it/strings.xml index 7641358e5b..6f322d844f 100755 --- a/auth/src/main/res/values-it/strings.xml +++ b/auth/src/main/res/values-it/strings.xml @@ -10,7 +10,7 @@ Twitter GitHub Telefono - Email + Email Accedi con Google Accedi con Google Accedi con Facebook @@ -36,7 +36,7 @@ Paese Seleziona un paese Cerca paese ad es. +1, "US" - Password + Password Nuova password Questo campo non può restare vuoto. L\'indirizzo email non è corretto diff --git a/auth/src/main/res/values-nb/strings.xml b/auth/src/main/res/values-nb/strings.xml index f422080c02..ede91bf8cf 100755 --- a/auth/src/main/res/values-nb/strings.xml +++ b/auth/src/main/res/values-nb/strings.xml @@ -62,7 +62,8 @@ Gjenopprett passordet Sjekk e-posten din Få instruksjoner sendt til denne e-postadressen for hvordan du tilbakestiller passordet ditt. - Send + + Send Følg veiledningen som er sendt til %1$s, for å gjenopprette passordet ditt. Sender… Denne e-postadressen samsvarer ikke med en eksisterende konto diff --git a/auth/src/main/res/values-no/strings.xml b/auth/src/main/res/values-no/strings.xml index 9af534059a..5ffff3ba74 100755 --- a/auth/src/main/res/values-no/strings.xml +++ b/auth/src/main/res/values-no/strings.xml @@ -62,7 +62,8 @@ Gjenopprett passordet Sjekk e-posten din Få instruksjoner sendt til denne e-postadressen for hvordan du tilbakestiller passordet ditt. - Send + + Send Følg veiledningen som er sendt til %1$s, for å gjenopprette passordet ditt. Sender… Denne e-postadressen samsvarer ikke med en eksisterende konto diff --git a/auth/src/main/res/values-pt-rPT/strings.xml b/auth/src/main/res/values-pt-rPT/strings.xml index 6296fb7da7..e1def2148b 100755 --- a/auth/src/main/res/values-pt-rPT/strings.xml +++ b/auth/src/main/res/values-pt-rPT/strings.xml @@ -10,7 +10,7 @@ Twitter GitHub Telemóvel - Email + Email Iniciar sessão com o Google Iniciar sessão com o Google Iniciar sessão com o Facebook @@ -31,7 +31,7 @@ Iniciar sessão com o Yahoo Iniciar sessão com o Yahoo Seguinte - Email + Email Número de telefone País Selecione um país diff --git a/auth/src/main/res/values-tl/strings.xml b/auth/src/main/res/values-tl/strings.xml index 7b6bd865a9..53040b7199 100755 --- a/auth/src/main/res/values-tl/strings.xml +++ b/auth/src/main/res/values-tl/strings.xml @@ -10,7 +10,7 @@ Twitter GitHub Telepono - Email + Email Mag-sign in sa Google Mag-sign in sa Google Mag-sign in sa Facebook @@ -36,7 +36,7 @@ Bansa Pumili ng bansa Maghanap ng bansa hal. +1, "US" - Password + Password Bagong password Hindi mo ito maaaring iwanan na walang laman. Mali ang email address na iyon diff --git a/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt b/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt index 7f01ec34fc..6a9788ef94 100644 --- a/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt +++ b/internal/lint/src/main/java/com/firebaseui/lint/internal/LintIssueRegistry.kt @@ -11,7 +11,8 @@ class LintIssueRegistry : IssueRegistry() { get() = com.android.tools.lint.detector.api.CURRENT_API override val issues = listOf( - NonGlobalIdDetector.NON_GLOBAL_ID + NonGlobalIdDetector.NON_GLOBAL_ID, + UntranslatedResourceDetector.UNTRANSLATED_RESOURCE ) override val vendor = Vendor( diff --git a/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt b/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt new file mode 100644 index 0000000000..120f5db832 --- /dev/null +++ b/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt @@ -0,0 +1,175 @@ +package com.firebaseui.lint.internal + +import com.android.SdkConstants.ATTR_NAME +import com.android.SdkConstants.ATTR_TRANSLATABLE +import com.android.SdkConstants.TAG_STRING +import com.android.SdkConstants.VALUE_FALSE +import com.android.ide.common.resources.configuration.FolderConfiguration +import com.android.resources.ResourceFolderType +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Context +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.Location +import com.android.tools.lint.detector.api.ResourceXmlDetector +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.XmlContext +import org.w3c.dom.Element + +/** + * Flags a string in a locale folder whose value is byte-identical to the base English one. + * + * `MissingTranslation` only fires when a string is *absent* from a locale, so a resource that + * was copied over untranslated is invisible to it: it is present, it just holds English. A + * whole class of shipped-in-English strings therefore never reaches a lint report; #2509 + * removed 269 such values, found by scanning rather than by any gate. + * + * Only `` elements are checked. An English copy of a `` or `` + * item is still invisible to this check as well as to `MissingTranslation`. + * + * English regional folders (`values-en-rGB` and friends) are skipped, as are strings marked + * `translatable="false"` and the [ALLOWED] names below. Anything else that is legitimately the + * same word in another language is suppressed at the site with `tools:ignore`, so the decision + * sits next to the string. + */ +class UntranslatedResourceDetector : ResourceXmlDetector() { + + /** Base `values/strings.xml` text, keyed by resource name. */ + private val baseStrings = mutableMapOf() + + /** Localized strings to judge once every resource file has been read. */ + private val localized = mutableListOf() + + private data class LocalizedString( + val name: String, + val folder: String, + val text: String, + val handle: Location.Handle + ) + + override fun appliesTo(folderType: ResourceFolderType): Boolean = + folderType == ResourceFolderType.VALUES + + override fun getApplicableElements(): List = listOf(TAG_STRING) + + override fun visitElement(context: XmlContext, element: Element) { + val name = element.getAttribute(ATTR_NAME) + if (name.isEmpty()) return + if (element.getAttribute(ATTR_TRANSLATABLE) == VALUE_FALSE) return + + // Compare rendered text rather than markup: a value that differs from the base only in + // its xliff placeholders is still untranslated copy. + val text = element.textContent.trim() + if (text.isEmpty()) return + if (!hasTranslatableWords(text)) return + + val folderName = context.file.parentFile?.name ?: return + + if (folderName == BASE_VALUES_FOLDER) { + // putIfAbsent rather than assignment: if a second source set ever contributes its + // own values/ folder, first-wins keeps the map from depending on traversal order. + baseStrings.putIfAbsent(name, text) + return + } + + // Only the base folder above seeds the comparison. Any other unqualified folder + // (values-v26, values-sw360dp, a future values-night) is a configuration variant of the + // English copy, not a translation, so it is neither a source nor a candidate. + val locale = FolderConfiguration.getConfigForFolder(folderName)?.localeQualifier ?: return + + if (locale.language == LANGUAGE_ENGLISH) return + if (name in ALLOWED) return + + localized += LocalizedString( + name = name, + folder = folderName, + text = text, + handle = context.createLocationHandle(element) + ) + } + + override fun afterCheckRootProject(context: Context) { + // Nothing to compare against when lint runs over a single file, which is the IDE's + // incremental mode. Reporting there would flag every locale string in the file. + if (baseStrings.isEmpty()) return + + for (string in localized) { + if (baseStrings[string.name] != string.text) continue + context.report( + UNTRANSLATED_RESOURCE, + string.handle.resolve(), + "\"${string.name}\" is the base English string in ${string.folder}, so it " + + "ships untranslated. Translate it, or mark it `tools:ignore=" + + "\"$ISSUE_ID\"` if the translation is genuinely identical." + ) + } + + baseStrings.clear() + localized.clear() + } + + /** + * Whether [text] contains anything a translator could change. + * + * `fui_tos_and_pp_footer` is two format specifiers separated by non-breaking spaces, so it + * is necessarily identical in all 84 locale folders that define it. Strings made only of + * placeholders, punctuation and whitespace have no words to translate. + * + * Escape sequences are stripped first because they are spelled with letters. A non-breaking + * space is written in these files as a backslash followed by u00A0, and the u and the A in + * that sequence would otherwise read as translatable content. + */ + private fun hasTranslatableWords(text: String): Boolean = + text.replace(UNICODE_ESCAPE, "") + .replace(FORMAT_SPECIFIER, "") + .any(Char::isLetter) + + companion object { + private const val ISSUE_ID = "UntranslatedResource" + private const val LANGUAGE_ENGLISH = "en" + private const val BASE_VALUES_FOLDER = "values" + + /** `%s`, `%d`, `%1$s` and friends. */ + private val FORMAT_SPECIFIER = Regex("""%(\d+\$)?[-#+ 0,(]*\d*(\.\d+)?[a-zA-Z%]""") + + /** Matches the escape sequence as written in the file, not the character it denotes. */ + private val UNICODE_ESCAPE = Regex("""\\u[0-9a-fA-F]{4}""") + + /** + * Names exempted wholesale, because suppressing them per folder would mean roughly 375 + * `tools:ignore` attributes. + * + * The four `fui_idp_name_*` entries are brand names, which are not translated. Note this + * does make a regression invisible: if a locale ever replaced one with a mistranslation, + * nothing here would report it. `values-fil` and `values-tl` already carry `Fecebook` + * for `fui_idp_name_facebook`, a pre-existing typo this exemption would hide. + * + * `fui_mfa_method_sms` is a **provisional** exemption and not the same kind of entry. + * "SMS" is genuinely translated in several locales (`رسالة نصية` in `ar`, `短信` in `zh`, + * `СМС` in `sr`), so the 70 folders that carry the bare English acronym are real hits + * this silences. It is exempted only because the string is unreferenced dead copy that + * CPRN-445 is expected to delete; when that lands, drop this entry rather than keeping + * it. + */ + private val ALLOWED = setOf( + "fui_idp_name_facebook", + "fui_idp_name_github", + "fui_idp_name_google", + "fui_idp_name_twitter", + "fui_mfa_method_sms" + ) + + val UNTRANSLATED_RESOURCE = Issue.create( + ISSUE_ID, + "Localized string still holds the base English text", + "A string that is present in a locale folder but identical to the base English " + + "value ships as English to users of that locale. `MissingTranslation` cannot " + + "catch this, because it only reports strings that are absent.", + Category.MESSAGES, + 6, + Severity.ERROR, + Implementation(UntranslatedResourceDetector::class.java, Scope.ALL_RESOURCES_SCOPE) + ) + } +} diff --git a/internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt b/internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt new file mode 100644 index 0000000000..471ca53ab9 --- /dev/null +++ b/internal/lint/src/test/java/com/firebaseui/lint/internal/UntranslatedResourceDetectorTest.kt @@ -0,0 +1,184 @@ +package com.firebaseui.lint.internal + +import com.android.tools.lint.checks.infrastructure.TestFiles.xml +import com.android.tools.lint.checks.infrastructure.TestLintTask +import com.firebaseui.lint.internal.UntranslatedResourceDetector.Companion.UNTRANSLATED_RESOURCE +import org.junit.Test + +class UntranslatedResourceDetectorTest { + + private fun configuredLint(): TestLintTask = TestLintTask.lint().withLocalSdk() + + private fun base(vararg entries: String) = xml( + "res/values/strings.xml", + """ + | + |${entries.joinToString("\n") { " $it" }} + |""".trimMargin() + ) + + private fun locale(folder: String, vararg entries: String) = xml( + "res/values-$folder/strings.xml", + """ + | + |${entries.joinToString("\n") { " $it" }} + |""".trimMargin() + ) + + @Test + fun `Passes on a translated string`() { + configuredLint() + .files( + base("""Sign in"""), + locale("fr", """Se connecter""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Fails on a string left as base English`() { + configuredLint() + .files( + base("""Authentication Error"""), + locale( + "zh-rTW", + """Authentication Error""" + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectErrorCount(1) + } + + @Test + fun `Passes on English regional folders`() { + configuredLint() + .files( + base("""Sign in"""), + locale("en-rGB", """Sign in""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes on allowlisted brand names`() { + configuredLint() + .files( + base("""Google"""), + locale("fr", """Google""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes on non-translatable strings`() { + configuredLint() + .files( + base( + """Sign in""", + """Internal""" + ), + locale( + "fr", + """Se connecter""", + """Internal""" + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes on a BCP47 locale folder with a real translation`() { + configuredLint() + .files( + base("""Sign in"""), + locale("b+es+419", """Iniciar sesión""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Fails on a BCP47 locale folder left as base English`() { + configuredLint() + .files( + base("""Sign in"""), + locale("b+es+419", """Sign in""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectErrorCount(1) + } + + @Test + fun `Passes on a placeholder-only string`() { + configuredLint() + .files( + base( + """Sign in""", + """%1${'$'}s \u00A0 %2${'$'}s""" + ), + locale( + "fr", + """Se connecter""", + """%1${'$'}s \u00A0 %2${'$'}s""" + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + @Test + fun `Passes when the string carries a tools ignore`() { + configuredLint() + .files( + base("""Password"""), + xml( + "res/values-it/strings.xml", + """ + | + | Password + |""".trimMargin() + ) + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } + + /** + * Only `values/` seeds the comparison. A string defined solely in a configuration variant is + * not a base string, so a locale copying it is not reported. + * + * The fixture deliberately keeps `fui_sign_in` out of `values/`: if the detector treated any + * unqualified folder as the base, `values-v26` would seed it and `values-fr` would report. + */ + @Test + fun `Does not treat a non-locale configuration folder as the base`() { + configuredLint() + .files( + base("""Unrelated"""), + xml( + "res/values-v26/strings.xml", + """ + | + | Sign in + |""".trimMargin() + ), + locale("fr", """Sign in""") + ) + .issues(UNTRANSLATED_RESOURCE) + .run() + .expectClean() + } +} From a7dbbe04b906d3002184015ab7da011c09cb3c89 Mon Sep 17 00:00:00 2001 From: Ademola Fadumo <48495111+demolaf@users.noreply.github.com> Date: Mon, 21 Sep 2026 18:55:17 +0100 Subject: [PATCH 3/3] fix(auth): spell the Facebook provider name correctly in Filipino and Tagalog --- auth/src/main/res/values-fil/strings.xml | 2 +- auth/src/main/res/values-tl/strings.xml | 2 +- .../lint/internal/UntranslatedResourceDetector.kt | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/auth/src/main/res/values-fil/strings.xml b/auth/src/main/res/values-fil/strings.xml index 0c582986c8..791f496ab0 100755 --- a/auth/src/main/res/values-fil/strings.xml +++ b/auth/src/main/res/values-fil/strings.xml @@ -6,7 +6,7 @@ %1$s \u00A0 \u00A0 %2$s Error sa network, tingnan ang koneksyon mo sa internet. Google - Fecebook + Facebook Twitter GitHub Telepono diff --git a/auth/src/main/res/values-tl/strings.xml b/auth/src/main/res/values-tl/strings.xml index 53040b7199..ac1f690d53 100755 --- a/auth/src/main/res/values-tl/strings.xml +++ b/auth/src/main/res/values-tl/strings.xml @@ -6,7 +6,7 @@ %1$s \u00A0 \u00A0 %2$s Error sa network, tingnan ang koneksyon mo sa internet. Google - Fecebook + Facebook Twitter GitHub Telepono diff --git a/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt b/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt index 120f5db832..03cfa28d6d 100644 --- a/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt +++ b/internal/lint/src/main/java/com/firebaseui/lint/internal/UntranslatedResourceDetector.kt @@ -142,8 +142,10 @@ class UntranslatedResourceDetector : ResourceXmlDetector() { * * The four `fui_idp_name_*` entries are brand names, which are not translated. Note this * does make a regression invisible: if a locale ever replaced one with a mistranslation, - * nothing here would report it. `values-fil` and `values-tl` already carry `Fecebook` - * for `fui_idp_name_facebook`, a pre-existing typo this exemption would hide. + * nothing here would report it. That is not hypothetical. `values-fil` and `values-tl` + * spelled `fui_idp_name_facebook` as `Fecebook` from the original translation import + * (#771) until this change corrected it, and an exemption is exactly why no gate would + * have caught it. * * `fui_mfa_method_sms` is a **provisional** exemption and not the same kind of entry. * "SMS" is genuinely translated in several locales (`رسالة نصية` in `ar`, `短信` in `zh`,